package service

import (
	"context"
	"fmt"
	log "github.com/sirupsen/logrus"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/mongo/options"
	v1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"strings"
	"time"
	"twwch/k8s-admin/apis"
	"twwch/k8s-admin/application/base"
	serviceRepo "twwch/k8s-admin/component/repo/service"
	"twwch/k8s-admin/models/common"
	"twwch/k8s-admin/models/service"
)

type ServiceService struct {
	base.BaseService
}

var svc = &ServiceService{}

func GetService() *ServiceService {
	svc.Logger = log.WithField("service", "service")
	return svc
}

func (svc *ServiceService) GetServiceName(name string) string {
	if strings.HasSuffix(name, "-service") {
		return name
	}
	return fmt.Sprintf("%v-service", name)
}

func (svc *ServiceService) Create(ctx context.Context, req *service.CreateReq) (*service.CreateResp, error) {
	serviceName := svc.GetServiceName(req.Name)
	resp := &service.CreateResp{
		IsOk: false,
	}
	if count, err := serviceRepo.GetRepoInstance().GetBase().Count(ctx, bson.M{
		"service_name": serviceName,
		"namespace":    req.Namespace,
	}); count > 0 || err != nil {
		if count > 0 {
			return nil, service.ServiceExists
		}
		svc.Logger.Error(ctx, err)
		return resp, err
	}
	serviceCreate := &v1.Service{
		ObjectMeta: metav1.ObjectMeta{
			Name:      serviceName,
			Namespace: req.Namespace,
		},
		Spec: v1.ServiceSpec{
			Type:     v1.ServiceType(req.Type),
			Selector: req.Selector,
			Ports:    req.Ports,
		},
	}
	created, err := apis.CoreV1().Services(req.Namespace).Create(ctx, serviceCreate, metav1.CreateOptions{})
	if err != nil {
		svc.Logger.Error(ctx, err)
		return resp, err
	}
	_, err = serviceRepo.GetRepoInstance().GetBase().UpdateOne(ctx, bson.M{
		"service_name": serviceName,
		"namespace":    req.Namespace,
	}, bson.M{
		"$set": bson.M{
			"service": created,
		},
		"$setOnInsert": bson.M{
			"create_time": time.Now(),
		},
	}, options.Update().SetUpsert(true))
	if err != nil {
		svc.Logger.Error(ctx, err)
	}
	return &service.CreateResp{
		Name:    serviceName,
		Service: created,
		IsOk:    true,
	}, nil
}

func (svc *ServiceService) Delete(ctx context.Context, req *service.DeleteReq) (*common.Empty, error) {
	serviceName := svc.GetServiceName(req.Name)
	if count, err := serviceRepo.GetRepoInstance().GetBase().Count(ctx, bson.M{
		"service_name": serviceName,
		"namespace":    req.Namespace,
	}); count == 0 || err != nil {
		if count == 0 {
			return nil, service.ServiceNotExists
		}
		svc.Logger.Error(ctx, err)
		return nil, err
	}
	err := apis.CoreV1().Services(req.Namespace).Delete(ctx, serviceName, metav1.DeleteOptions{})
	if err != nil {
		svc.Logger.Error(ctx, err)
	}
	_, err = serviceRepo.GetRepoInstance().GetBase().DeleteOne(ctx, bson.M{
		"service_name": serviceName,
		"namespace":    req.Namespace,
	})
	if err != nil {
		svc.Logger.Error(ctx, err)
	}
	return nil, err
}

func (svc *ServiceService) List(ctx context.Context, req *service.ListReq) (*service.ListResp, error) {
	filters := bson.M{}
	if req.Namespace != "" {
		filters["namespace"] = req.Namespace
	}
	if req.Name != "" {
		filters["service_name"] = bson.M{"$regex": req.Name, "$options": "$i"}
	}
	ops := options.Find().SetSort(bson.M{"namespace": 1})
	services := make([]*service.Service, 0)
	err := serviceRepo.GetRepoInstance().GetBase().Find(ctx, filters, &services, ops)
	if err != nil {
		svc.Logger.Error(ctx, err)
		return nil, err
	}
	count, err := serviceRepo.GetRepoInstance().GetBase().Count(ctx, filters)
	if err != nil {
		svc.Logger.Error(ctx, err)
		return nil, err
	}
	return &service.ListResp{
		Total: count,
		Items: services,
	}, nil
}
